import { Recorder } from "./Recorder";

import * as puppeteer from 'puppeteer';
import * as fs from 'fs';

interface DanmuMessage {
  honorLevel?: number;       // 荣誉等级
  fansLevel?: number;        // 灯牌等级
  userId: string;            // 用户ID
  content: MessageContent[]; // 消息内容（文本+表情）
  rawHtml?: string;          // 原始HTML(调试用)
  timestamp: Date;           // 时间戳
}

type MessageContent = {
  type: 'text' | 'emoji';
  value: string;
};

/**
 * 提取数字从图片URL中的特定格式
 * @param url 图片URL
 * @param pattern 需要匹配的模式
 * @returns 提取的数字或undefined
 */
function extractLevelFromUrl(url: string, pattern: string): number | undefined {
  const regex = new RegExp(`${pattern}_(\\d+)`);
  const match = url.match(regex);
  return match ? parseInt(match[1], 10) : undefined;
}

/**
 * 解析单条弹幕消息
 * @param element 弹幕DOM元素
 * @returns 解析后的弹幕消息对象
 */
async function parseDanmuElement(element: puppeteer.ElementHandle): Promise<DanmuMessage> {
  // 获取原始HTML供调试使用
  const rawHtml = await element.evaluate(el => el.outerHTML);
  
  // 解析荣誉等级
  const honorImgSelector = 'img[src*="new_user_grade_level_v1_"]';
  const honorElement = await element.$(honorImgSelector);
  let honorLevel: number | undefined;
  
  if (honorElement) {
    const honorSrc = await honorElement.evaluate(img => img.getAttribute('src') || '');
    honorLevel = extractLevelFromUrl(honorSrc, 'new_user_grade_level_v1');
  }
  
  // 解析灯牌等级
  const fansImgSelector = 'img[src*="fansclub_new_advanced_badge_"]';
  const fansElement = await element.$(fansImgSelector);
  let fansLevel: number | undefined;
  
  if (fansElement) {
    const fansSrc = await fansElement.evaluate(img => img.getAttribute('src') || '');
    fansLevel = extractLevelFromUrl(fansSrc, 'fansclub_new_advanced_badge');
  }
  
  // 提取用户ID (找到以冒号结尾的span)
  const userIdElement = await element.$('span.u2QdU6ht, span[class*="ht"]');
  const userId = userIdElement 
    ? await userIdElement.evaluate(span => span.textContent?.replace('：', '') || '') 
    : '未知用户';
  
  // 解析消息内容(文本和表情)
  const contentElement = await element.$('span.WsJsvMP9, span[class*="MP9"]');
  let contentItems: MessageContent[] = [];
  
  if (contentElement) {
    // 获取所有文本节点和表情元素
    contentItems = await contentElement.evaluate(span => {
      const items: MessageContent[] = [];
      
      // 处理文本内容
      const textElement = span.querySelector('.webcast-chatroom___content-with-emoji-text, [class*="emoji-text"]');
      if (textElement && textElement.textContent) {
        items.push({
          type: 'text',
          value: textElement.textContent
        });
      }
      
      // 处理表情内容
      const emojiElements = span.querySelectorAll('.webcast-chatroom___content-with-emoji-emoji img, [class*="emoji-emoji"] img');
      emojiElements.forEach(emoji => {
        const altText = (emoji as HTMLImageElement).alt || '[表情]';
        items.push({
          type: 'emoji',
          value: altText
        });
      });
      
      return items;
    });
  }
  
  return {
    honorLevel,
    fansLevel,
    userId,
    content: contentItems,
    rawHtml,
    timestamp: new Date()
  };
}

/**
 * 监听新的弹幕消息并进行处理
 * @param page Puppeteer页面对象
 * @param callback 处理弹幕的回调函数
 */
async function monitorDanmu(
  page: puppeteer.Page, 
  callback: (message: DanmuMessage) => void
) {
  // 监听DOM变化，检测新弹幕
  await page.evaluate(() => {
    const targetNode = document.querySelector('.webcast-chatroom, [class*="chatroom"]');
    if (!targetNode) return;
    
    const observer = new MutationObserver(mutations => {
      for (const mutation of mutations) {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          for (const node of Array.from(mutation.addedNodes)) {
            if (node instanceof HTMLElement && 
                (node.classList.contains('webcast-chatroom___item-wrapper') || 
                 node.className.includes('item-wrapper'))) {
              // 将新弹幕标记为未处理
              node.setAttribute('data-processed', 'false');
            }
          }
        }
      }
    });
    
    observer.observe(targetNode, { childList: true, subtree: true });
  });
  
  // 定时处理未处理的弹幕
  const processInterval = setInterval(async () => {
    try {
      const unprocessedElements = await page.$$('[data-processed="false"], .webcast-chatroom___item-wrapper:not([data-processed])');
      
      for (const element of unprocessedElements) {
        // 标记为已处理
        await element.evaluate(el => el.setAttribute('data-processed', 'true'));
        
        // 解析并回调
        const danmuMessage = await parseDanmuElement(element);
        callback(danmuMessage);
      }
    } catch (error) {
      console.error('处理弹幕时出错:', error);
    }
  }, 1000); // 每秒检查一次
  
  return () => clearInterval(processInterval); // 返回清理函数
}

/**
 * 将弹幕消息格式化为可读形式
 * @param message 弹幕消息对象
 * @returns 格式化后的字符串
 */
function formatDanmuMessage(message: DanmuMessage): string {
  const honorText = message.honorLevel ? `(${message.honorLevel})` : '';
  const fansText = message.fansLevel ? `(${message.fansLevel})` : '';
  
  const contentText = message.content.map(item => {
    if (item.type === 'text') return item.value;
    return `[${item.value}]`;
  }).join('');
  
  return `${honorText}${fansText} ${message.userId}：${contentText}`;
}

/**
 * 主函数
 * @param url 抖音直播URL
 * @param duration 爬取持续时间(毫秒)
 */
async function main(url: string, duration: number = 300000) {
  // 创建输出目录
  const outputDir = './danmu_data';
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir);
  }
  
  // 准备输出文件
  const timestamp = new Date().toISOString().replace(/:/g, '-');
  const outputFile = `${outputDir}/danmu_${timestamp}.json`;
  const readableFile = `${outputDir}/danmu_${timestamp}.txt`;
  
  // 初始化数据
  const danmuData: DanmuMessage[] = [];
  
  // 启动浏览器
  const browser = await puppeteer.launch({
    headless: false, // 设为true开启无头模式
    args: ['--no-sandbox', '--disable-setuid-sandbox', '--window-size=1280,720']
  });
  
  console.log('正在打开抖音直播页面...');
  const page = await browser.newPage();
  
  // 设置用户代理和视窗
  await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
  await page.setViewport({ width: 1280, height: 720 });
  
  try {
    // 导航到直播页面
    await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
    console.log('页面加载完成，开始监听弹幕...');
    
    // 等待弹幕容器加载
    await page.waitForSelector('.webcast-chatroom, [class*="chatroom"]', { timeout: 10000 });
    
    // 监听弹幕
    const stopMonitoring = await monitorDanmu(page, (message) => {
      danmuData.push(message);
      const formattedMessage = formatDanmuMessage(message);
      console.log(formattedMessage);
      fs.appendFileSync(readableFile, formattedMessage + '\n');
    });
    
    // 设置定时器关闭
    console.log(`将在${duration / 1000}秒后停止爬取`);
    await new Promise(resolve => setTimeout(resolve, duration));
    
    // 停止监听并保存数据
    stopMonitoring();
    fs.writeFileSync(outputFile, JSON.stringify(danmuData, null, 2));
    
    console.log(`爬取完成，共收集${danmuData.length}条弹幕`);
    console.log(`数据已保存至：${outputFile}`);
    console.log(`可读格式已保存至：${readableFile}`);
    
  } catch (error) {
    console.error('爬取过程中出错:', error);
  } finally {
    await browser.close();
  }
}

// 使用示例
if (require.main === module) {
  const liveUrl = process.argv[2] || 'https://live.douyin.com/某直播间ID';
  const duration = parseInt(process.argv[3], 10) || 300000; // 默认5分钟
  
  main(liveUrl, duration).catch(console.error);
}

export { 
  main, 
  parseDanmuElement, 
  monitorDanmu, 
  formatDanmuMessage, 
  DanmuMessage, 
  MessageContent 
};


export class DamuRecorder extends Recorder {
    private browser: puppeteer.Browser | null = null;
    private page: puppeteer.Page | null = null;
    private stopMonitoring: (() => void) | null = null;
    private outputDir: string = './danmu_data';
    private danmuData: DanmuMessage[] = [];
    private outputFile: string;
    private readableFile: string;

    constructor(private url: string, private duration: number = 300000) {
        super();
        const timestamp = new Date().toISOString().replace(/:/g, '-');
        this.outputFile = `${this.outputDir}/danmu_${timestamp}.json`;
        this.readableFile = `${this.outputDir}/danmu_${timestamp}.txt`;
    }

    async start(): Promise<void> {
        // 创建输出目录
        if (!fs.existsSync(this.outputDir)) {
            fs.mkdirSync(this.outputDir);
        }

        // 启动浏览器
        this.browser = await puppeteer.launch({
            headless: false,
            args: ['--no-sandbox', '--disable-setuid-sandbox', '--window-size=1280,720']
        });

        console.log('正在打开抖音直播页面...');
        this.page = await this.browser.newPage();

        // 设置用户代理和视窗
        await this.page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
        await this.page.setViewport({ width: 1280, height: 720 });

        try {
            await this.page.goto(this.url, { waitUntil: 'networkidle2', timeout: 30000 });
            console.log('页面加载完成，开始监听弹幕...');

            await this.page.waitForSelector('.webcast-chatroom, [class*="chatroom"]', { timeout: 10000 });

            this.stopMonitoring = await monitorDanmu(this.page, (message) => {
                this.danmuData.push(message);
                const formattedMessage = formatDanmuMessage(message);
                console.log(formattedMessage);
                fs.appendFileSync(this.readableFile, formattedMessage + '\n');
            });

            console.log(`将在${this.duration / 1000}秒后停止爬取`);
            setTimeout(() => this.stop(), this.duration);

        } catch (error) {
            console.error('爬取过程中出错:', error);
            await this.cleanup();
            throw error;
        }
    }

    async stop(): Promise<void> {
        if (this.stopMonitoring) {
            this.stopMonitoring();
        }

        // 保存数据
        fs.writeFileSync(this.outputFile, JSON.stringify(this.danmuData, null, 2));

        console.log(`爬取完成，共收集${this.danmuData.length}条弹幕`);
        console.log(`数据已保存至：${this.outputFile}`);
        console.log(`可读格式已保存至：${this.readableFile}`);

        await this.cleanup();
    }

    private async cleanup(): Promise<void> {
        if (this.browser) {
            await this.browser.close();
            this.browser = null;
            this.page = null;
        }
    }
}